import { Page } from '@/models';
import { arrayFormater, FormatOption } from '@/utils';
import { isEmpty } from '@/utils/is';
import { Result } from '@/utils/request/type';
import useLoadMore from '@/utils/request/useLoadMore';
import { ConfigProvider, Empty, Select, SelectProps, Spin } from 'antd';
import {
  FC,
  MutableRefObject,
  useCallback,
  useContext,
  useImperativeHandle,
  useMemo,
  useState
} from 'react';

export type RequestFn<T extends Record<string, unknown>> = (
  params: T & {
    pageSize: number;
    pageNum: number;
  }
) => Result<Page<T>>;

export interface BaseLoadMoreSelectActionType<T = any> {
  getOptions: () => T[];
}
export interface BaseSelectProps extends SelectProps<any> {
  actionRef?: MutableRefObject<BaseLoadMoreSelectActionType | undefined>;
  multiple?: boolean;
  request: RequestFn<any>;
  params?: Record<string, any>;
  beforeOptions?: Record<string, any>[];
  defaultSelectedOptions?: Record<string, any>[];
  searchFieldName?: string;
  debounceInterval?: number;
  fieldNames?: FormatOption;
}
const BaseSelect: FC<BaseSelectProps> = (props) => {
  const {
    request,
    actionRef,
    params,
    searchFieldName = 'name',
    defaultSelectedOptions = [],
    fieldNames,
    debounceInterval = 300,
    beforeOptions = [],
    multiple,
    showArrow = true,
    dropdownRender,
    onChange,
    allowClear = true,
    ...anyProps
  } = props;
  const activeFieldNames = useMemo(
    () => ({ label: 'label', value: 'value', disabled: 'disabled', ...fieldNames }),
    [fieldNames]
  );
  const [search, setSearch] = useState<string | undefined>();
  const req = useLoadMore(request, {
    defaultParams: [
      {
        ...params,
        [searchFieldName]: search
      }
    ],
    manual: anyProps.disabled,
    refreshDeps: [params, search, searchFieldName],
    debounceInterval,
    isNoMore: (d) => (d ? d.list.length >= d.rowCount : false)
  });
  const handleSearch = useCallback((value: string) => {
    setSearch(value);
  }, []);

  const { value: defaultValue } = anyProps;
  const showOptions = useMemo(() => {
    if (search) {
      return req.data?.list || [];
    }
    const options = [...beforeOptions, ...(req.data?.list || [])];
    const moreValue: (string | number)[] = [];
    if (multiple) {
      (defaultValue || []).forEach((v: any) => {
        const value = req.data?.list.find((i: any) => i[activeFieldNames.value] === v);
        if (!value) {
          moreValue.push(v);
        }
      });
    } else {
      const value = req.data?.list.find((i: any) => i[activeFieldNames.value] === defaultValue);
      if (!value) {
        if (defaultValue) {
          moreValue.push(defaultValue);
        }
      }
    }
    if (moreValue.length > 0 && defaultValue) {
      options.push(
        {
          [activeFieldNames.value]: '...',
          [typeof activeFieldNames.label === 'function' ? 'label' : activeFieldNames.label]: '...',
          [activeFieldNames.disabled]: true
        },
        ...defaultSelectedOptions.filter(
          (i) =>
            moreValue.find((v) => v === i[activeFieldNames.value]) &&
            !isEmpty(i[activeFieldNames.value])
        )
      );
    }
    return options;
  }, [
    activeFieldNames,
    multiple,
    req.data?.list,
    search,
    beforeOptions,
    defaultValue,
    defaultSelectedOptions
  ]);
  useImperativeHandle(actionRef, () => ({
    getOptions: () => showOptions
  }));
  const provider = useContext(ConfigProvider.ConfigContext);
  return (
    <Select
      placeholder='请选择'
      mode={multiple ? 'multiple' : undefined}
      {...anyProps}
      loading={req.loading}
      onChange={(v) => {
        if (onChange) {
          if (multiple) {
            onChange(
              v,
              showOptions.filter((i) => v.includes(i[activeFieldNames.value]))
            );
          } else {
            onChange(
              v,
              showOptions.find((i) => i[activeFieldNames.value] === v)
            );
          }
        }
      }}
      showSearch
      allowClear={allowClear}
      showArrow={showArrow}
      searchValue={search}
      filterOption={false}
      notFoundContent={
        <Spin spinning={req.loading}>
          {provider.renderEmpty ? (
            provider.renderEmpty()
          ) : (
            <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
          )}
        </Spin>
      }
      onSearch={handleSearch}
      onPopupScroll={(e) => {
        const { target } = e;
        const { scrollTop, offsetHeight, scrollHeight } = target as HTMLDivElement;
        if (scrollHeight - (scrollTop + offsetHeight) < 10 && !req.loadingMore) {
          req.loadMore();
        }
      }}
      options={arrayFormater(showOptions, activeFieldNames) as any}
      dropdownRender={(menu) => {
        const dom = (
          <div>
            {menu}
            <div style={{ textAlign: 'center' }}>
              <Spin spinning={req.loadingMore} delay={200} />
            </div>
          </div>
        );
        return dropdownRender ? dropdownRender(dom) : dom;
      }}
    />
  );
};

export default BaseSelect;
